use super::role::RoleVo;
use super::Result;
use crate::entity::{prelude::SysResource, sys_role_resource};
use crate::entity::{sys_resource, sys_role};
use crate::util;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, DbConn, DerivePartialModel, EntityTrait,
    FromQueryResult, PaginatorTrait, PartialModelTrait, QueryFilter, QuerySelect, Set,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

pub struct Resource;

/// 资源的详细数据
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysResource")]
pub struct ResourceVo {
    pub id: i32,
    pub path: String,
    pub method: String,
    pub description: String,
    pub status: bool,
    pub created_at: chrono::NaiveDateTime,
    pub updated_at: chrono::NaiveDateTime,
}

impl From<sys_resource::Model> for ResourceVo {
    fn from(value: sys_resource::Model) -> Self {
        Self {
            id: value.id,
            path: value.path,
            method: value.method,
            description: value.description,
            status: value.status,
            created_at: value.created_at,
            updated_at: value.updated_at,
        }
    }
}

/// 资源提交的数据
#[derive(Debug, Deserialize, Validate)]
pub struct ResourceDo {
    #[validate(length(min = 1, max = 255), non_control_character)]
    pub path: String,
    #[validate(length(max = 32))]
    pub method: Option<String>,
    #[validate(length(max = 255))]
    pub description: Option<String>,
    pub status: Option<bool>,
}

impl Into<sys_resource::ActiveModel> for ResourceDo {
    /// 将提交数据转换成[sys_resource::ActiveModel]
    fn into(self) -> sys_resource::ActiveModel {
        sys_resource::ActiveModel {
            path: Set(self.path),
            method: self.method.map(Set).unwrap_or_default(),
            description: self.description.map(Set).unwrap_or_default(),
            status: self.status.map(Set).unwrap_or_default(),
            ..Default::default()
        }
    }
}

impl Resource {
    ane_macros::model_list!(
        #model=sys_resource
        #order=[Path:Asc]
        /// 分页列出资源
        /// # params
        /// - `path:Option<String>` 包含path
        /// - `status:Option<bool>` true启用，false停用
        pub fn list(Path.contains(path:Option<String>), Status.eq(status:Option<bool>))
    );

    ane_macros::model_fetch!(
        #model=sys_resource
        pub fn get_by_id(Id.eq(id: i32))
    );

    ane_macros::model_fetch!(
        #model=sys_resource
        pub fn get_by_path(Path.eq(path: &str))
    );

    pub async fn create<C>(db: &C, data: ResourceDo) -> Result<ResourceVo>
    where
        C: ConnectionTrait,
    {
        let res: sys_resource::ActiveModel = data.into();
        res.insert(db).await.map(Into::into)
    }

    /// 根据id更新资源, 当id不存在时，抛出错误
    pub async fn update<C>(db: &C, id: i32, data: ResourceDo) -> Result<ResourceVo>
    where
        C: ConnectionTrait,
    {
        let mut res: sys_resource::ActiveModel = data.into();
        res.id = Set(id);
        res.update(db).await.map(Into::into)
    }

    /// 根据id删除资源, 当id不存在时，抛出错误
    pub async fn delete<C>(db: &C, id: i32) -> Result<sea_orm::DeleteResult>
    where
        C: ConnectionTrait,
    {
        sys_role_resource::Entity::delete_many()
            .filter(sys_role_resource::Column::ResourceId.eq(id))
            .exec(db)
            .await?;
        sys_resource::Entity::delete_by_id(id).exec(db).await
    }

    /// 获得资源关联的角色， 不检查对应的resource是否存在
    /// - `id: i32` 资源id
    pub async fn get_role(db: &DbConn, id: i32) -> Result<Vec<RoleVo>> {
        sys_role::Entity::find()
            .left_join(sys_role_resource::Entity)
            .filter(sys_role_resource::Column::ResourceId.eq(id))
            .into_partial_model()
            .all(db)
            .await
    }
}
